<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="context包">
<meta name="twitter:description" content="<p>Context的功能介绍：接口；Context树：4种类型ctx的生成和使用；Context的使用原则</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/gotour-pkg-context.html"/>
<meta property="og:title" content="来玩魔王的咚 | context包" />
<meta property="og:description" content="<p>Context的功能介绍：接口；Context树：4种类型ctx的生成和使用；Context的使用原则</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>Context的功能介绍：接口；Context树：4种类型ctx的生成和使用；Context的使用原则</p>" />

    <meta name="tags" content="gotour" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li class="active"><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2020-12-02 15:00:00+08:00">
                <script>document.write(moment('2020-12-02 15:00:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2020-12-02 15:00:00+08:00">
                <script>document.write(moment('2020-12-03 13:00:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            context包
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#gong-neng-jie-shao">功能介绍</a><ul>
<li><a href="#jie-kou">接口</a></li>
</ul>
</li>
<li><a href="#sheng-cheng-context-shu">生成 Context 树</a><ul>
<li><a href="#emptyctx">emptyCtx</a></li>
<li><a href="#cancelctx">cancelCtx</a></li>
<li><a href="#timerctx">timerCtx</a></li>
<li><a href="#valuectx">valueCtx</a></li>
</ul>
</li>
<li><a href="#context-shi-yong-yuan-ze">Context 使用原则</a></li>
</ul>
</div>
<p>sync.WaitGroup类型是一个实现一对多goroutine协作流程的同步工具。这篇的context也是一种这类工具，功能更强大。  </p>
<h3 id="gong-neng-jie-shao"><a class="toclink" href="#gong-neng-jie-shao">功能介绍</a></h3>
<p>context主要用来在goroutine之间传递上下文信息，控制多个goroutine之间的协作。具备以下功能：</p>
<ol>
<li>取消信号，手动停止goroutine</li>
<li>超时时间，超过（就是经过的时间）自动停止goroutine</li>
<li>截止时间，同上，到达某个时间点自动停止</li>
<li>传值，设置一个key-value对，通过key可以获取到value的值</li>
</ol>
<p>context是可以嵌套使用的，并且一般也是嵌套用的。父context停止了，下游的所有子context也都会停止。父context设置的键值对，下游的子context都能够通过key来获取到value。<br>
关键就是嵌套后传递的功能，这是其他工具替代不了的。我们只需要关注当前的goroutine和下游的goroutine，至于下游还要继续往下游传递，或者开分支再往下传递，都不必担心，取消信号能传递到节点下的整个分支。上游的值下游也能递归的取到。  </p>
<p><strong>小结</strong>，终于搞明白是干什么用的了，似乎就两点：</p>
<ol>
<li>手动或自动发送取消信号，并且信号能一直往下游传递</li>
<li>获取设置的键值对，并且能逐层往上游找</li>
</ol>
<h4 id="jie-kou"><a class="toclink" href="#jie-kou">接口</a></h4>
<p>context.Conext接口的源码如下：</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">Context</span><span class="w"> </span><span class="kd">interface</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 返回 context 的自动取消时间（deadline）和是否有设置取消（第二个参数）</span><span class="w"></span>
<span class="w">    </span><span class="nx">Deadline</span><span class="p">()</span><span class="w"> </span><span class="p">(</span><span class="nx">deadline</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">,</span><span class="w"> </span><span class="nx">ok</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 当 context 被取消或者到了 deadline，返回一个被关闭的 channel</span><span class="w"></span>
<span class="w">    </span><span class="nx">Done</span><span class="p">()</span><span class="w"> </span><span class="o">&lt;-</span><span class="kd">chan</span><span class="w"> </span><span class="kd">struct</span><span class="p">{}</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 在 channel Done 关闭后，返回 context 取消原因</span><span class="w"></span>
<span class="w">    </span><span class="nx">Err</span><span class="p">()</span><span class="w"> </span><span class="kt">error</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 获取 key 对应的 value</span><span class="w"></span>
<span class="w">    </span><span class="nx">Value</span><span class="p">(</span><span class="nx">key</span><span class="w"> </span><span class="kd">interface</span><span class="p">{})</span><span class="w"> </span><span class="kd">interface</span><span class="p">{}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>接口定义了4个方法，它们都是幂等的，即多次调用同一个方法，得到的结果是一样的：</p>
<ul>
<li>Deadline(): 是截止时间，到了时间点会自动发送取消信号，第二个返回值表示是否设置了截止时间。</li>
<li>Done(): 返回一个只读的channel，就是接收取消信号用的。</li>
<li>Err(): 返回error接口，表示channel被关闭的原因。例如是被取消，还是超时。</li>
<li>Value(): 提供key获取对应的value。</li>
</ul>
<h3 id="sheng-cheng-context-shu"><a class="toclink" href="#sheng-cheng-context-shu">生成 Context 树</a></h3>
<p>使用的时候，不需要自己实现接口，context包中有提供函数生成各种功能的Context。生成一个新的context需要提供一个父context，这样就会生成一颗context树。父context发出取消信号时，子context也会发出，一直往下游传递下去。对一个context获取键值对时，会逐层向上递归的查找，直到找到key，或者最终找到根节点返回nil。  </p>
<h4 id="emptyctx"><a class="toclink" href="#emptyctx">emptyCtx</a></h4>
<p>空 Context，不可取消，没有截止时间，也不能存储键值对。一般就是用来作为Context树的根节点的。<br>
包里直接示例化了两个变量（变量名小写不可导出），并且分别提供了两个函数来返回这两个空Context：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="nx">background</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nb">new</span><span class="p">(</span><span class="nx">emptyCtx</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">todo</span><span class="w">       </span><span class="p">=</span><span class="w"> </span><span class="nb">new</span><span class="p">(</span><span class="nx">emptyCtx</span><span class="p">)</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">Background</span><span class="p">()</span><span class="w"> </span><span class="nx">Context</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">background</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">TODO</span><span class="p">()</span><span class="w"> </span><span class="nx">Context</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="nx">todo</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p>源码很简单，除了名字以外都是一样的。使用时候具体的差别就参考官方的建议了：<br>
Background通常被用于主函数、初始化以及测试中，作为一个顶层的context，就是用作根；<br>
而TODO是在不确定用哪个Context，或者还没有可以用的Context的时候，先用着的。  </p>
<p>使用根节点创建Context示例：</p>
<div class="highlight"><pre><span></span><code><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">cancel</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithCancel</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Background</span><span class="p">())</span><span class="w"></span>
</code></pre></div>

<p>空Context是作为参数的<code>context.Background()</code>。这里的<code>context.WithCancel()</code>是用来生成一个可取消的Context的，下面会讲。要生成一个Context，需要提供一个父Context。第一个Context就使用<code>context.Background()</code>作为父Context，也就是根Context。  </p>
<h4 id="cancelctx"><a class="toclink" href="#cancelctx">cancelCtx</a></h4>
<p>可取消的 Context，可以发送取消信号，并且下游的子Context也会收到取消信号。<br>
用WithCancel函数用来创建一个可取消的context。还是上节的代码：</p>
<div class="highlight"><pre><span></span><code><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">cancel</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithCancel</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Background</span><span class="p">())</span><span class="w"></span>
<span class="c1">// 发送取消信号，直接调用返回的函数</span><span class="w"></span>
<span class="nx">cancel</span><span class="p">()</span><span class="w"></span>
</code></pre></div>

<p>函数有两个返回值。第一个是新的Context，第二个是发送取消信号的函数，直接调用就能发信号。  </p>
<p>下面是一个代码示例，创建了一个Room，然后Room中有两个Table，每个Table有两个Player。关闭Table后，Table中的Player都会退出。关闭Roome后，所有Table中的Player都会退出：</p>
<div class="highlight"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>

<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;context&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;fmt&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;sync&quot;</span><span class="w"></span>
<span class="w">    </span><span class="s">&quot;time&quot;</span><span class="w"></span>
<span class="p">)</span><span class="w"></span>

<span class="kd">var</span><span class="w"> </span><span class="nx">start</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="w"></span>

<span class="c1">// player 没秒钟报告一次，如果收到取消信号，报告退出后返回</span><span class="w"></span>
<span class="kd">func</span><span class="w"> </span><span class="nx">player</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">select</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Done</span><span class="p">():</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%7.4f %s: 退出。\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">Seconds</span><span class="p">(),</span><span class="w"> </span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="k">return</span><span class="w"></span>
<span class="w">        </span><span class="k">default</span><span class="p">:</span><span class="w"></span>
<span class="w">            </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%7.4f %s: 进行中...\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">Seconds</span><span class="p">(),</span><span class="w"> </span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">            </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="p">}</span><span class="w"></span>
<span class="w">    </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w">  </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">start</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 创建一个Room的Context</span><span class="w"></span>
<span class="w">    </span><span class="nx">ctxRoom</span><span class="p">,</span><span class="w"> </span><span class="nx">cancelRoom</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithCancel</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Background</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 在Room下创建两个Table的Context</span><span class="w"></span>
<span class="w">    </span><span class="nx">ctxTableA</span><span class="p">,</span><span class="w"> </span><span class="nx">cancelTableA</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithCancel</span><span class="p">(</span><span class="nx">ctxRoom</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="nx">cancelTableA</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">ctxTableB</span><span class="p">,</span><span class="w"> </span><span class="nx">cancelTableB</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithCancel</span><span class="p">(</span><span class="nx">ctxRoom</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="nx">cancelTableB</span><span class="p">()</span><span class="w"></span>

<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">wg</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 创建Player A1，属于TableA</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">player</span><span class="p">(</span><span class="nx">ctxTableA</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;A1&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 创建Player A2，属于TableA</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">player</span><span class="p">(</span><span class="nx">ctxTableA</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;A2&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 创建Player B1，属于TableB</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">player</span><span class="p">(</span><span class="nx">ctxTableB</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;B1&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 创建Player B2，属于TableB</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">player</span><span class="p">(</span><span class="nx">ctxTableB</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;B2&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>
<span class="w">    </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%7.4f 关闭 TableA\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">Seconds</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="nx">cancelTableA</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">time</span><span class="p">.</span><span class="nx">Sleep</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">3</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%7.4f 关闭 Room\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">Seconds</span><span class="p">())</span><span class="w"></span>
<span class="w">    </span><span class="nx">cancelRoom</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 等待所有goroutine结束</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Wait</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%7.4f 结束。\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">Seconds</span><span class="p">())</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<p><strong>必须调用cancel</strong>确保发送取消信号，否则会有内存泄漏的问题。上面的例子中，是不需要关闭cancelTableB的，因为直接把上游的cancelRoom关闭了。但是代码中不能这么写：</p>
<div class="highlight"><pre><span></span><code><span class="nx">ctxTableB</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithCancel</span><span class="p">(</span><span class="nx">ctxRoom</span><span class="p">)</span><span class="w"></span>
</code></pre></div>

<p>编译器会报错，不能忽略cancel函数的返回值。当然也不能传给一个变量就不用了，命名的变量都必须要使用。上面示例中的应该就是建议的做法，都应该紧跟一个defer，确保返回的函数会被调用，函数被多次调用不会有问题。  </p>
<h4 id="timerctx"><a class="toclink" href="#timerctx">timerCtx</a></h4>
<p>可定时的 Context，基于可取消的Context，多了一个定时自动取消的功能。有两种定时的方式，提供到期时间点（Deadline），或者设置超时时间。不过内部记录的都是Deadline。设置的超时时间会转成Deadline：</p>
<div class="highlight"><pre><span></span><code><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">().</span><span class="nx">Add</span><span class="p">(</span><span class="nx">timeout</span><span class="p">)</span><span class="w"></span>
</code></pre></div>

<p>对于两个设置方式，分别提供了2个函数生成Context：</p>
<div class="highlight"><pre><span></span><code><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">cancel</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithTimeout</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Background</span><span class="p">(),</span><span class="w"> </span><span class="mi">5</span><span class="o">*</span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">)</span><span class="w"></span>
<span class="k">defer</span><span class="w"> </span><span class="nx">cancel</span><span class="p">()</span><span class="w"></span>

<span class="nx">deadline</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">ParseInLocation</span><span class="p">(</span><span class="s">&quot;2006-01-02 15:04&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;2020-12-03 11:09&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Local</span><span class="p">)</span><span class="w"></span>
<span class="nx">ctx2</span><span class="p">,</span><span class="w"> </span><span class="nx">cancel2</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithDeadline</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Background</span><span class="p">(),</span><span class="w"> </span><span class="nx">deadline</span><span class="p">)</span><span class="w"></span>
<span class="k">defer</span><span class="w"> </span><span class="nx">cancel2</span><span class="p">()</span><span class="w"></span>
</code></pre></div>

<p>WithTimeout比较常用。另外返回的取消函数，都要显示的调用一下，建议就是用defer。<br>
WithDeadline用的时候，设成的时间注意时区问题。如果用<code>time.Parse</code>返回的UTC时间，这里用了<code>time.ParseInLocation</code>并设置了本地使用的时区。  </p>
<h4 id="valuectx"><a class="toclink" href="#valuectx">valueCtx</a></h4>
<p>值 Context，在父Context的基础上，还携带了一个键值对。这样Context不仅可以取消，还可以传值。<br>
使用的时候，选择一个父Context创建，在那个基础上，增加一个键值对，生成新的Context。设置值和取出值的用法如下：</p>
<div class="highlight"><pre><span></span><code><span class="nx">ctx</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithValue</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Background</span><span class="p">(),</span><span class="w"> </span><span class="s">&quot;key&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;value&quot;</span><span class="p">)</span><span class="w"></span>
<span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="s">&quot;key&quot;</span><span class="p">)</span><span class="w"></span>
</code></pre></div>

<p>这里还有个小问题，就是key的类型不好，这里不影响使用，但是建议优化。<br>
关于key的类型，源码中的注释里也有说明，为了避免包之间可能使用一样的key，不建议使用内置的类型来做key。<br>
比如一个ctx会在不同的包之间传递，这边加了一个key是name，另一个包里也加了一个叫name的key。这样就把别人定义的key覆盖掉了。<br>
要避免这种情况，先在包中定义不可导出的自定义类型，使用这个类型来做key：</p>
<div class="highlight"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">ctxKey</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
<span class="nx">ctx</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithValue</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Background</span><span class="p">(),</span><span class="w"> </span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;key&quot;</span><span class="p">),</span><span class="w"> </span><span class="s">&quot;value&quot;</span><span class="p">)</span><span class="w"></span>
<span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;key&quot;</span><span class="p">))</span><span class="w"></span>
</code></pre></div>

<p>这样，虽然底层还是字符串，但是因为类型不同，会被认作是不同的key。并且由于是包中不可导出的类型，所以包之外不会生成同样的key。  </p>
<p>在之前的示例的基础上，修改主函数部分，用了定时和传值的功能：</p>
<div class="highlight"><pre><span></span><code><span class="kd">var</span><span class="w"> </span><span class="nx">start</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="w"></span>
<span class="c1">// 定义一个不可导出的类型，作为valueCtx的key</span><span class="w"></span>
<span class="kd">type</span><span class="w"> </span><span class="nx">ctxKey</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>

<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">    </span><span class="nx">start</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="kd">var</span><span class="w"> </span><span class="nx">wg</span><span class="w"> </span><span class="nx">sync</span><span class="p">.</span><span class="nx">WaitGroup</span><span class="w"></span>
<span class="w">    </span><span class="c1">// 创建一个Room的Context，忽略时间解析错误</span><span class="w"></span>
<span class="w">    </span><span class="nx">deadline</span><span class="p">,</span><span class="w"> </span><span class="nx">_</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">ParseInLocation</span><span class="p">(</span><span class="s">&quot;2006-01-02 15:04&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;2020-12-03 20:48&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Local</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">ctxRoom</span><span class="p">,</span><span class="w"> </span><span class="nx">cancelRoom</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithDeadline</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">Background</span><span class="p">(),</span><span class="w"> </span><span class="nx">deadline</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="nx">cancelRoom</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">ctxRoom</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithValue</span><span class="p">(</span><span class="nx">ctxRoom</span><span class="p">,</span><span class="w"> </span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;room&quot;</span><span class="p">),</span><span class="w"> </span><span class="s">&quot;SH&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// 接收Room关闭的信号，打印消息</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="o">&lt;-</span><span class="nx">ctxRoom</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%7.4f 关闭 Room %s\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">Seconds</span><span class="p">(),</span><span class="w"> </span><span class="nx">ctxRoom</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;room&quot;</span><span class="p">)))</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>

<span class="w">    </span><span class="c1">// 在Room下创建两个Table的Context</span><span class="w"></span>
<span class="w">    </span><span class="nx">ctxTableA</span><span class="p">,</span><span class="w"> </span><span class="nx">cancelTableA</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithTimeout</span><span class="p">(</span><span class="nx">ctxRoom</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="o">*</span><span class="mi">3</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="nx">cancelTableA</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">ctxTableA</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithValue</span><span class="p">(</span><span class="nx">ctxTableA</span><span class="p">,</span><span class="w"> </span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;table&quot;</span><span class="p">),</span><span class="w"> </span><span class="s">&quot;TabelA&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="o">&lt;-</span><span class="nx">ctxTableA</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%7.4f 关闭 %s\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">Seconds</span><span class="p">(),</span><span class="w"> </span><span class="nx">ctxTableA</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;table&quot;</span><span class="p">)))</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>

<span class="w">    </span><span class="nx">ctxTableB</span><span class="p">,</span><span class="w"> </span><span class="nx">cancelTableB</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithTimeout</span><span class="p">(</span><span class="nx">ctxRoom</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="o">*</span><span class="mi">300</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="nx">cancelTableB</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">ctxTableB</span><span class="w"> </span><span class="p">=</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">WithValue</span><span class="p">(</span><span class="nx">ctxTableB</span><span class="p">,</span><span class="w"> </span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;table&quot;</span><span class="p">),</span><span class="w"> </span><span class="s">&quot;TabelB&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="o">&lt;-</span><span class="nx">ctxTableB</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%7.4f 关闭 %s\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">Seconds</span><span class="p">(),</span><span class="w"> </span><span class="nx">ctxTableB</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;table&quot;</span><span class="p">)))</span><span class="w"></span>
<span class="w">    </span><span class="p">}()</span><span class="w"></span>

<span class="w">    </span><span class="c1">// 创建Player</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fullName</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%s_%s_%s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;room&quot;</span><span class="p">)),</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;table&quot;</span><span class="p">)),</span><span class="w"> </span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">player</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">fullName</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}(</span><span class="nx">ctxTableA</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;P1&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fullName</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%s_%s_%s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;room&quot;</span><span class="p">)),</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;table&quot;</span><span class="p">)),</span><span class="w"> </span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">player</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">fullName</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}(</span><span class="nx">ctxTableA</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;P2&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fullName</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%s_%s_%s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;room&quot;</span><span class="p">)),</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;table&quot;</span><span class="p">)),</span><span class="w"> </span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">player</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">fullName</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}(</span><span class="nx">ctxTableB</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;P3&quot;</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Add</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="k">go</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">ctx</span><span class="w"> </span><span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w">        </span><span class="nx">fullName</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">&quot;%s_%s_%s&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;room&quot;</span><span class="p">)),</span><span class="w"> </span><span class="nx">ctx</span><span class="p">.</span><span class="nx">Value</span><span class="p">(</span><span class="nx">ctxKey</span><span class="p">(</span><span class="s">&quot;table&quot;</span><span class="p">)),</span><span class="w"> </span><span class="nx">name</span><span class="p">)</span><span class="w"></span>
<span class="w">        </span><span class="k">defer</span><span class="w"> </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Done</span><span class="p">()</span><span class="w"></span>
<span class="w">        </span><span class="nx">player</span><span class="p">(</span><span class="nx">ctx</span><span class="p">,</span><span class="w"> </span><span class="nx">fullName</span><span class="p">)</span><span class="w"></span>
<span class="w">    </span><span class="p">}(</span><span class="nx">ctxTableB</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;P4&quot;</span><span class="p">)</span><span class="w"></span>

<span class="w">    </span><span class="c1">// 等待所有goroutine结束</span><span class="w"></span>
<span class="w">    </span><span class="nx">wg</span><span class="p">.</span><span class="nx">Wait</span><span class="p">()</span><span class="w"></span>
<span class="w">    </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Printf</span><span class="p">(</span><span class="s">&quot;%7.4f 结束。\n&quot;</span><span class="p">,</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Since</span><span class="p">(</span><span class="nx">start</span><span class="p">).</span><span class="nx">Seconds</span><span class="p">())</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</code></pre></div>

<h3 id="context-shi-yong-yuan-ze"><a class="toclink" href="#context-shi-yong-yuan-ze">Context 使用原则</a></h3>
<p>要用好Context有一些使用原则，要尽量遵守：</p>
<ol>
<li>Context不要放在结构体中，要直接以参数的方式传递。</li>
<li>Context作为函数的参数传时，要放在参数的第一个位置，命名就用ctx。</li>
<li>使用context.Backgroud函数生成根节点。</li>
<li>valueCtx少用，尽量别用，只传递必要的值。这其实是一个低效率的链表，并且层层传递后，也不知道拿到的值是哪一步加上的，中间是否有被覆盖过</li>
</ol>
<p>最后，要清楚Context是线程安全的，可以放心使用。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>